package com.jichangxiu.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.jichangxiu.common.annotation.Dynamic;
import com.jichangxiu.common.constant.Constants;
import com.jichangxiu.common.entity.bo.ValidateBos;
import com.jichangxiu.common.exception.ServiceException;
import com.jichangxiu.common.utils.ConvertUtils;
import com.jichangxiu.framework.service.impl.BaseServiceImpl;
import com.jichangxiu.framework.utils.NoUtils;
import com.jichangxiu.framework.utils.SecurityUtils;
import com.jichangxiu.framework.utils.ValidateUtils;
import com.jichangxiu.system.entity.bo.SysDeptBo;
import com.jichangxiu.system.entity.bo.SysUserBo;
import com.jichangxiu.system.entity.dto.add.AddSysDept;
import com.jichangxiu.system.entity.dto.edit.EditSysDept;
import com.jichangxiu.system.entity.dto.query.QuerySysDept;
import com.jichangxiu.system.entity.po.SysDept;
import com.jichangxiu.system.entity.po.SysPostDept;
import com.jichangxiu.system.entity.po.SysUser;
import com.jichangxiu.system.entity.vo.SysDeptVo;
import com.jichangxiu.system.mapper.SysDeptMapper;
import com.jichangxiu.system.service.SysDeptService;
import com.jichangxiu.system.service.SysPostDeptService;
import com.jichangxiu.system.service.SysUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.validation.constraints.NotBlank;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * SysDeptServiceImpl:
 *
 * @author JiChangXiu
 * @create 2024-06-28 13:44:22
 */
@Service
public class SysDeptServiceImpl extends BaseServiceImpl<SysDeptMapper, SysDept, SysDeptVo> implements SysDeptService {

    @Resource
    private SysUserService sysUserService;

    @Resource
    private SysPostDeptService sysPostDeptService;

    /**
     * 新增系统部门
     *
     * @param addSysDept 新增系统部门视图对象
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(AddSysDept addSysDept) {
        return getBaseMapper().insert(addAndEditValidBo(ConvertUtils.convert(addSysDept, SysDeptBo.class))) > 0;
    }

    /**
     * 单（多）选删除系统部门
     *
     * @param idList 系统部门主键列表
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean del(List<String> idList) {
        return getBaseMapper().deleteBatchIds(delValid(idList)) > 0;
    }

    /**
     * 修改系统部门
     *
     * @param editSysDept 修改系统部门视图对象
     * @return 是否成功
     */
    @Dynamic
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean edit(EditSysDept editSysDept) {
        return getBaseMapper().updateById(addAndEditValidBo(ConvertUtils.convert(editSysDept, SysDeptBo.class))) > 0;
    }

    /**
     * 根据主键查询系统部门
     *
     * @param id       系统部门主键
     * @param isExtend 是否扩展
     * @return 系统部门详情
     */
    @Dynamic
    @Override
    public SysDeptVo info(@NotBlank(message = "主键不能为空") String id, Boolean isExtend) {
        SysDeptVo sysDeptVo = getBaseMapper().selectJoinOne(SysDeptVo.class, buildQueryWrapper(QuerySysDept.builder().id(id).build()));
        if (Boolean.TRUE.equals(isExtend) && ObjectUtil.isNotEmpty(sysDeptVo))
            sysDeptVo = renderSysDeptVoList(ListUtil.toList(sysDeptVo)).get(0);
        return sysDeptVo;
    }

    /**
     * （可分页）查询系统部门列表
     *
     * @param querySysDept 查询视图对象
     * @return （分页）系统部门列表
     */
    @Dynamic
    @Override
    public Page<SysDeptVo> lists(QuerySysDept querySysDept) {
        Page<SysDeptVo> sysDeptVoPage = getBaseMapper().selectJoinPage(startPage(), SysDeptVo.class, buildQueryWrapper(querySysDept));
        if (Boolean.TRUE.equals(querySysDept.getIsExtend()))
            sysDeptVoPage.setRecords(renderSysDeptVoList(sysDeptVoPage.getRecords()));
        return sysDeptVoPage;
    }

    /**
     * 新增 / 修改 前校验
     *
     * @param sysDeptBo 校验用业务对象
     * @return 数据库操作对象
     */
    private SysDept addAndEditValidBo(SysDeptBo sysDeptBo) {
        SysDept sysDept = ConvertUtils.convert(sysDeptBo, SysDept.class);
        if (ObjectUtil.isNull(sysDept))
            throw new ServiceException("待校验对象为空");
        if (!Validator.isEmail(sysDept.getEmail()))
            throw new ServiceException("邮箱不符合规范");
        if (!Validator.isMobile(sysDept.getPhone()))
            throw new ServiceException("手机号不符合规范");
        if (StrUtil.isBlank(sysDept.getParentId()))
            sysDept.setParentId("0");
        List<ValidateBos> validateBosList = Stream.of(ValidateUtils.createNeValidateBos(ValidateUtils.createValidateBo(SysDept::getId, sysDept.getId(), "主键")), ValidateUtils.createOrEqValidateBos(ValidateUtils.createValidateBo(SysDept::getNo, sysDept.getNo(), "编码"), ValidateUtils.createValidateBo(SysDept::getCode, sysDept.getCode(), "标识"), ValidateUtils.createValidateBo(SysDept::getName, sysDept.getName(), "名称"), ValidateUtils.createValidateBo(SysDept::getRemark, sysDept.getRemark(), "备注"))).collect(Collectors.toList());
        // 唯一校验
        ValidateUtils.uniqueValidate(this::countWrapper, validateBosList);
        if (StrUtil.isEmpty(sysDept.getId())) {
            // 新增
            if (Constants.TEN_SUPER_DEPT.equals(sysDept.getCode()))
                throw new ServiceException("标识不能为 " + Constants.TEN_SUPER_DEPT);
            if (Constants.SYS_SUPER_DEPT.equals(sysDept.getCode()))
                throw new ServiceException("标识不能为 " + Constants.SYS_SUPER_DEPT);
            sysDept.setNo(NoUtils.createNo(SecurityUtils.getTenantId(), "dept"));
            sysDept.setTenantId(SecurityUtils.getTenantId());
            return sysDept;
        } else {
            // 修改
            SysDept dbSysDept = getById(sysDept.getId());
            if (!dbSysDept.getCode().equals(sysDept.getCode()) && Constants.TEN_SUPER_DEPT.equals(sysDept.getCode()))
                throw new ServiceException("标识不能为 " + Constants.TEN_SUPER_DEPT);
            if (!dbSysDept.getEnabled().equals(sysDept.getEnabled()) && Constants.TEN_SUPER_DEPT.equals(sysDept.getCode()))
                throw new ServiceException("标识为 " + Constants.TEN_SUPER_DEPT + " 的部门状态不能修改");
            if (!dbSysDept.getCode().equals(sysDept.getCode()) && Constants.SYS_SUPER_DEPT.equals(sysDept.getCode()))
                throw new ServiceException("标识不能为 " + Constants.SYS_SUPER_DEPT);
            if (!dbSysDept.getEnabled().equals(sysDept.getEnabled()) && Constants.SYS_SUPER_DEPT.equals(sysDept.getCode()))
                throw new ServiceException("标识为 " + Constants.SYS_SUPER_DEPT + " 的部门状态不能修改");
            // 当修改了启用状态，并且是不启用得情况下
            if (!dbSysDept.getEnabled().equals(sysDept.getEnabled()) && !sysDept.getEnabled()) {
                // 如果有子部门不能修改状态
                if (count(new LambdaQueryWrapper<SysDept>().in(SysDept::getParentId, sysDept.getId())) > 0)
                    throw new ServiceException(sysDept.getName() + "该部门下还存在子部门，不能修改状态为禁用");
                // 如果被用户关联不能修改状态
                if (sysUserService.count(new LambdaQueryWrapper<SysUser>().in(SysUser::getDeptId, sysDept.getId())) > 0)
                    throw new ServiceException(sysDept.getName() + "该部门已被用户关联，不能修改状态为禁用");
            }
            // 父类 不能是自己
            if (!dbSysDept.getParentId().equals(sysDept.getParentId()) && sysDept.getParentId().equals(dbSysDept.getId()))
                throw new ServiceException(sysDept.getName() + "该部门的父部门不能是自己");
            BeanUtil.copyProperties(sysDept, dbSysDept, true);
            return dbSysDept;
        }
    }

    /**
     * 删除前校验
     *
     * @param idList 主键列表
     * @return 主键列表
     */
    private List<String> delValid(List<String> idList) {
        if (ObjectUtil.isNull(idList))
            throw new ServiceException("待校验主键列表为空");
        List<SysDeptBo> dbSysDeptBoList = selectJoinList(SysDeptBo.class, buildQueryWrapper(QuerySysDept.builder().idList(idList).build()));
        dbSysDeptBoList.forEach(sysDeptBo -> {
            if (Constants.TEN_SUPER_DEPT.equals(sysDeptBo.getCode()))
                throw new ServiceException("标识为 " + Constants.TEN_SUPER_DEPT + " 的部门不能删除");
            if (Constants.SYS_SUPER_DEPT.equals(sysDeptBo.getCode()))
                throw new ServiceException("标识为 " + Constants.SYS_SUPER_DEPT + " 的部门不能删除");
        });
        // 填充子部门
        List<SysDeptBo> childSysDeptBoList = ConvertUtils.convert(list(new LambdaQueryWrapper<SysDept>().in(SysDept::getParentId, idList)), SysDeptBo.class);
        dbSysDeptBoList.forEach(sysDeptBo -> sysDeptBo.setChildSysDeptBoList(ObjectUtil.isNotNull(childSysDeptBoList) ? childSysDeptBoList.stream().filter(childSysDeptBo -> sysDeptBo.getId().equals(childSysDeptBo.getParentId())).collect(Collectors.toList()) : new ArrayList<>()));
        // 根据部门ID列表查询对应的用户列表
        List<SysUserBo> sysUserBoList = ConvertUtils.convert(sysUserService.list(new LambdaQueryWrapper<SysUser>().in(SysUser::getDeptId, idList)), SysUserBo.class);
        dbSysDeptBoList.forEach(sysDeptBo -> sysDeptBo.setSysUserBoList(ObjectUtil.isNotNull(sysUserBoList) ? sysUserBoList.stream().filter(sysUserBo -> sysDeptBo.getId().equals(sysUserBo.getDeptId())).collect(Collectors.toList()) : new ArrayList<>()));
        // 根据部门ID列表查询对应的角色列表
        List<SysPostDept> sysPostDeptList = sysPostDeptService.list(new LambdaQueryWrapper<SysPostDept>().in(SysPostDept::getDeptId, idList));
        dbSysDeptBoList.forEach(sysDeptBo -> sysDeptBo.setSysPostDeptList(ObjectUtil.isNotNull(sysPostDeptList) ? sysPostDeptList.stream().filter(sysPostDept -> sysDeptBo.getId().equals(sysPostDept.getDeptId())).collect(Collectors.toList()) : new ArrayList<>()));
        dbSysDeptBoList.forEach(sysDeptBo -> {
            // 校验部门下有子部门不能删除
            // 如果某个部门下还存在子部门，则部门不能删除
            if (ObjectUtil.isNotEmpty(sysDeptBo.getChildSysDeptBoList()))
                throw new ServiceException(sysDeptBo.getName() + "该部门下还存在子部门，不能删除");
            // 校验部门下有用户不能删除
            // 如果某个部门下还存在用户，则部门不能删除
            if (ObjectUtil.isNotEmpty(sysDeptBo.getSysUserBoList()))
                throw new ServiceException(sysDeptBo.getName() + "该部门下还存在用户，不能删除");
            // 校验部门下被角色关联不能删除
            // 如果某个部门还被角色关联，则部门不能删除
            if (ObjectUtil.isNotEmpty(sysDeptBo.getSysPostDeptList()))
                throw new ServiceException(sysDeptBo.getName() + "该部门还被岗位关联，不能删除");
        });
        return idList;
    }

    /**
     * 渲染视图
     *
     * @param sysDeptVoList 视图列表
     * @return 渲染后的视图列表
     */
    private List<SysDeptVo> renderSysDeptVoList(List<SysDeptVo> sysDeptVoList) {
        if (ObjectUtil.isNotEmpty(sysDeptVoList)) {
            // todo 扩展信息填入
        }
        return sysDeptVoList;
    }

    /**
     * 构建查询条件
     *
     * @param querySysDept 查询用业务对象
     * @return 查询条件
     */
    private MPJLambdaWrapper<SysDept> buildQueryWrapper(QuerySysDept querySysDept) {
        MPJLambdaWrapper<SysDept> lambdaQueryWrapper = new MPJLambdaWrapper<SysDept>().selectAll(SysDept.class).leftJoin(SysUser.class, "u", SysUser::getId, SysDept::getUserId, ext -> ext.selectAs(SysUser::getName, SysDeptVo::getUserName)).disableSubLogicDel();
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysDept.getId()), SysDept::getId, querySysDept.getId());
        lambdaQueryWrapper.in(ObjectUtil.isNotEmpty(querySysDept.getIdList()), SysDept::getId, querySysDept.getIdList());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysDept.getNo()), SysDept::getNo, querySysDept.getNo());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysDept.getCode()), SysDept::getCode, querySysDept.getCode());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysDept.getName()), SysDept::getName, querySysDept.getName());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysDept.getPhone()), SysDept::getPhone, querySysDept.getPhone());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysDept.getEmail()), SysDept::getEmail, querySysDept.getEmail());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysDept.getParentId()), SysDept::getParentId, querySysDept.getParentId());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysDept.getUserId()), SysDept::getUserId, querySysDept.getUserId());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysDept.getSort()), SysDept::getSort, querySysDept.getSort());
        lambdaQueryWrapper.like(StrUtil.isNotEmpty(querySysDept.getRemark()), SysDept::getRemark, querySysDept.getRemark());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysDept.getEnabled()), SysDept::getEnabled, querySysDept.getEnabled());
        lambdaQueryWrapper.eq(StrUtil.isNotEmpty(querySysDept.getEnabledBy()), SysDept::getEnabledBy, querySysDept.getEnabledBy());
        lambdaQueryWrapper.eq(ObjectUtil.isNotEmpty(querySysDept.getEnabledTime()), SysDept::getEnabledTime, querySysDept.getEnabledTime());
        lambdaQueryWrapper.apply(StrUtil.isNotEmpty(querySysDept.getScope()), querySysDept.getScope());
        lambdaQueryWrapper.orderByAsc(SysDept::getSort);
        return lambdaQueryWrapper;
    }

}